package com.only4play.practice.invoice.core.service.impl;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.only4play.codegen.support.base.EntityOperations;
import com.only4play.codegen.support.exception.BusinessException;
import com.only4play.codegen.support.model.PageRequestWrapper;
import com.only4play.practice.common.enums.invoice.InvoiceKind;
import com.only4play.practice.common.enums.ReceiptStatus;
import com.only4play.practice.common.enums.ValidStatus;
import com.only4play.practice.invoice.api.model.creator.OrderReceiptCreator;
import com.only4play.practice.invoice.api.model.query.OrderReceiptQuery;
import com.only4play.practice.invoice.api.model.updater.OrderReceiptUpdater;
import com.only4play.practice.invoice.api.model.vo.OrderReceiptVO;
import com.only4play.practice.invoice.core.domainservice.model.exchange.ExchangeInvoiceModel;
import com.only4play.practice.invoice.core.domainservice.model.exchange.ExchangeInvoiceResultModel;
import com.only4play.practice.invoice.core.domainservice.model.exchange.InvoiceModel;
import com.only4play.practice.invoice.core.entity.Invoice;
import com.only4play.practice.invoice.core.enums.InvoiceErrorCode;
import com.only4play.practice.invoice.core.envents.InvoiceEvents;
import com.only4play.practice.invoice.core.facade.IFlowNoFacade;
import com.only4play.practice.invoice.core.mapper.InvoiceMapper;
import com.only4play.practice.ordercommon.pay.PayItem;
import com.only4play.practice.invoice.api.model.OrderRegisterModel;
import com.only4play.practice.invoice.api.model.OrderRegisterResultModel;
import com.only4play.practice.invoice.core.domainservice.IOrderReceiptDomainService;
import com.only4play.practice.invoice.core.entity.OrderReceipt;
import com.only4play.practice.invoice.core.envents.OrderReceiptEvents;
import com.only4play.practice.invoice.core.mapper.OrderReceiptMapper;
import com.only4play.practice.invoice.core.service.IOrderReceiptService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
    * <p>
    * 订单发票实体表 服务实现类
    * </p>
*
* @author mdy
* @since 2024-09-15
*/
@Transactional
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderReceiptServiceImpl extends ServiceImpl<OrderReceiptMapper, OrderReceipt> implements IOrderReceiptService {

    private final IOrderReceiptDomainService orderReceiptDomainService;

    private final ApplicationEventPublisher publisher;

    private final IFlowNoFacade flowNoFacade;

    private final OrderReceiptMapper orderReceiptMapper;

    private final InvoiceMapper invoiceMapper;
    @Override
    public Long createOrderReceipt(OrderRegisterModel model) {

        // TODO
        OrderRegisterResultModel resultModel = orderReceiptDomainService.orderRegister(model);

        OrderReceipt orderReceipt = publishOrderRegisterEvent(model, resultModel);

        boolean save = this.save(orderReceipt);
        if (save) {
            return orderReceipt.getId();
        }

        throw new RuntimeException("create order receipt fail");
    }

    /**
     * 订单注册为凭据
     *
     * @param registerModel
     * @param resultModel
     */
    public OrderReceipt publishOrderRegisterEvent(OrderRegisterModel registerModel,
                                                  OrderRegisterResultModel resultModel) {
        OrderReceipt orderReceipt = new OrderReceipt();
        orderReceipt.setValidStatus(ValidStatus.VALID);
        orderReceipt.setReceiptStatus(ReceiptStatus.UNMAKING);
        if (IterUtil.isNotEmpty(resultModel.getValidList())) {
            orderReceipt.setValidList(resultModel.getValidList());
            long validAmount = resultModel.getValidList()
                    .stream()
                    .map(PayItem::getMoney)
                    .reduce(BigDecimal.ZERO, NumberUtil::add).longValue();
            orderReceipt.setValidAmount(validAmount);
        } else {
            orderReceipt.setValidList(Collections.emptyList());
            orderReceipt.setValidAmount(0L);
        }
        if (IterUtil.isNotEmpty(resultModel.getInvalidList())) {
            orderReceipt.setInvalidList(resultModel.getInvalidList());
            long invalidAmount = resultModel.getInvalidList()
                    .stream()
                    .map(PayItem::getMoney)
                    .reduce(BigDecimal.ZERO, NumberUtil::add).longValue();
            orderReceipt.setInvalidAmount(invalidAmount);
        } else {
            orderReceipt.setInvalidList(Collections.emptyList());
            orderReceipt.setInvalidAmount(0L);
        }
        orderReceipt.setTaxCode(resultModel.getTaxCode());
        publisher.publishEvent(new OrderReceiptEvents.OrderRegisterEvent(orderReceipt, registerModel, resultModel));
        return orderReceipt;
    }
    @Override
    public void updateOrderReceipt(OrderReceiptUpdater updater) {

    }

    @Override
    public void validOrderReceipt(Long id) {

    }

    @Override
    public void invalidOrderReceipt(Long id) {

    }

    @Override
    public OrderReceiptVO findById(Long id) {
        return null;
    }

    @Override
    public Page<OrderReceiptVO> findByPage(PageRequestWrapper<OrderReceiptQuery> query) {
        return null;
    }

    /**
     * 凭据换取发票 -> 本质上就是凭据的更新操作
     * @param exchangeInvoiceModel
     */
    @Override
    public void exchangeInvoice(ExchangeInvoiceModel exchangeInvoiceModel) {
        ExchangeInvoiceResultModel resultModel = orderReceiptDomainService.exchangeInvoice(
                exchangeInvoiceModel);
        List<Long> flowNos = exchangeInvoiceModel.getFlowNos();
        exchangeInvoiceModel.setBatchNo(String.valueOf(flowNoFacade.getNextId()));
        //从性能上讲可以优化为批量操作，对于对象本身来讲，开票行为属于每个个体，技术和现实需要折中处理,这里要避免事件嵌套
        //将凭据批量更新为开票中，同时生成发票信息
        // List<OrderReceipt> receiptList = orderReceiptRepository.findAllByRegisterFlowNoIn(
        //         flowNos);
        QueryWrapper<OrderReceipt> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("register_flow_no",flowNos);
        List<OrderReceipt> receiptList = orderReceiptMapper.selectList(queryWrapper);

        List<OrderReceipt> notUnMakingList = receiptList.stream()
                .filter(r -> !Objects.equals(r.getReceiptStatus(), ReceiptStatus.UNMAKING)).collect(
                        Collectors.toList());
        if(IterUtil.isNotEmpty(notUnMakingList)){
            //如果存在不是未开票的则报错,并且将列表返回去
            throw new BusinessException(InvoiceErrorCode.INVOICE_NOT_ALLOW, notUnMakingList);
        }
        //批量修改为开票中
        receiptList.stream().forEach(r -> r.setReceiptStatus(ReceiptStatus.MAKING));
        this.saveBatch(receiptList);

        resultModel.getInvoiceList().forEach(invoiceModel -> {
            Invoice invoice = new Invoice();
            invoice.setApplyFlowNo(String.valueOf(flowNoFacade.getNextId()));
            EntityOperations
                    .doCreate(invoiceMapper)
                    .create(() -> invoice)
                    .update(e -> doCreate(invoice,exchangeInvoiceModel,invoiceModel))
                    .execute();
        });

    }

    /**
     * 发票生成
     * @param exchangeInvoiceModel
     * @param invoiceModel
     */
    public void doCreate(Invoice invoice,ExchangeInvoiceModel exchangeInvoiceModel, InvoiceModel invoiceModel) {
        invoice.setBatchNo(exchangeInvoiceModel.getBatchNo());
        invoice.setValidStatus(ValidStatus.VALID);
        invoice.setIndexNo(invoiceModel.getIndexNo());

        invoice.setCustomerAccount(exchangeInvoiceModel.getCustomerModel().getCustomerAccount());
        invoice.setCustomerName(exchangeInvoiceModel.getCustomerModel().getCustomerName());
        invoice.setCustomerOpenBank(exchangeInvoiceModel.getCustomerModel().getCustomerOpenBank());
        invoice.setCustomerPhone(exchangeInvoiceModel.getCustomerModel().getCustomerPhone());
        invoice.setCustomerTaxNo(exchangeInvoiceModel.getCustomerModel().getCustomerTaxNo());
        invoice.setCustomerAddress(exchangeInvoiceModel.getCustomerModel().getCustomerAddress());

        invoice.setSellerAccount(invoiceModel.getSellerModel().getSellerAccount());
        invoice.setSellerAddress(invoiceModel.getSellerModel().getSellerAddress());
        invoice.setSellerOpenBank(invoiceModel.getSellerModel().getSellerOpenBank());
        invoice.setSellerName(invoiceModel.getSellerModel().getSellerName());
        invoice.setSellerPhone(invoiceModel.getSellerModel().getSellerPhone());
        invoice.setSellerTaxNo(invoiceModel.getSellerModel().getSellerTaxNo());
        //默认为蓝票
        invoice.setInvoiceKind(InvoiceKind.BLUE_TICKET);
        invoice.setInvoiceType(exchangeInvoiceModel.getInvoiceType());
        invoice.setTitleType(exchangeInvoiceModel.getTitleType());
        invoice.setApplyChannelNo(exchangeInvoiceModel.getApplyChannelCode());
        BigDecimal totalValidAmount = invoiceModel.getReceiptDocuments()
                .stream()
                .map(m -> m.getValidAmount())
                .reduce(BigDecimal.ZERO, (a, b) -> NumberUtil.add(a, b));
        invoice.setInvoiceAmount(totalValidAmount);
        invoice.setDrawer(invoiceModel.getOperateUserModel().getDrawer());
        invoice.setPayee(invoiceModel.getOperateUserModel().getPayee());
        invoice.setReviewer(invoiceModel.getOperateUserModel().getReviewer());
        invoice.setTaxCode(invoiceModel.getReceiptDocuments().get(0).getTaxCode());
        invoice.setExtAttrs(exchangeInvoiceModel.getExtAttrs());
        publisher.publishEvent(new InvoiceEvents.InvoiceCreateEvent(invoice, exchangeInvoiceModel, invoiceModel));

    }

}
